/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package grex;

import grex.DT.tree.DecisionTreeBuilder;
import grex.genes.Gene;
import grex.genes.GeneException;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author RIK
 */
public class GPEnsemble extends GP {

    private ArrayList<GP> ensemble;

    public GPEnsemble(Environment environment, ArrayList<GP> ensemble) throws BNFException {
        super(environment);
        this.ensemble = ensemble;
        getHead();

    }
    public GPEnsemble(Environment environment,int nMembers) throws GeneException, BNFException {
        super(environment);
        DecisionTreeBuilder builder = new DecisionTreeBuilder(environment);
        builder.buildRandomForrest(nMembers);
        ensemble = builder.getForrestAsArrayList();
        getHead();
    }
    public GPEnsemble(Environment environment,int nMembers, boolean useOneOfSplits,double scoreThreshold,double entropyThreshold,double percentageOfAttributes) throws GeneException, BNFException {
        super(environment);
        DecisionTreeBuilder builder = new DecisionTreeBuilder(environment, useOneOfSplits, scoreThreshold,entropyThreshold,percentageOfAttributes);
        builder.buildRandomForrest(nMembers);
        ensemble = builder.getForrestAsArrayList();
        getHead();
    }

    public void addMember(GP gp) {
        ensemble.add(gp);
    }

    public void removeMember(GP gp) {
        ensemble.remove(gp);
    }

    public GP[] getMembers(){
        GP[] members = new GP[ensemble.size()];
        return ensemble.toArray(members);
    }


    @Override
    public Gene getHead() {
        int index = (int) Math.floor(ensemble.size() * Options.rnd.nextDouble());
        head = ensemble.get(index).getHead();
        return head;
    }

    @Override
    public List getTreeAsList() {
        List tree = new ArrayList();
        getTreeAsList(tree, getHead());
        return tree;
    }

    @Override
    public void calcNrOfNodes() {
        length = 0;
        for (GP gp : ensemble) {
            length += calcNrOfNodes(gp.getHead());
        }
        length = length / ensemble.size();
    }

    @Override
    public void execute(PredictionContainer pc)  {
        PredictionContainer pcTmp = pc.clone();
        for (Prediction p : pc.values()) {
            p.setSupport(new double[environment.getNrOfTargetCategories()]);
            p.resetPredictionCalculations();
        }

        for (GP gp : ensemble) {
            gp.execute(pcTmp);
            for (Prediction pTmp : pcTmp.values()) {
                pc.get(pTmp.getInstance()).getSupport()[(int) pTmp.getPrediction()]++;
            }
        }
    }

    
    public synchronized void train() {
        for (Prediction p : pcTrain.values()) {
            p.setSupport(new double[environment.getNrOfTargetCategories()]);
            p.resetPredictionCalculations();
        }
        for (GP gp : ensemble) {
            gp.train();            
            for (Prediction memberPred : gp.getPcTrain().values()) {
                pcTrain.get(memberPred.getInstance()).getSupport()[(int) memberPred.getPrediction()]++;
            }
        }
        resetStats();
    }

    @Override
    public synchronized void optimize() throws GeneException {
        for (GP gp : ensemble) {
            gp.optimize();
        }
        train();
    }

    @Override
    public synchronized void clean() {
        for (GP gp : ensemble) {
            gp.clean();
        }
    }

    @Override
    public Object clone(){
        ArrayList<GP> ensClone = new ArrayList<GP>();
        for(GP member:ensemble)
            ensClone.add((GP)member.clone());
        GPEnsemble gpEns=null;
        try {
            gpEns = new GPEnsemble(environment, ensClone);
            copyToNewGPEnsemble(gpEns);
        } catch (BNFException ex) {
            ErrorManager.getInstance().reportError(ex);
        }
        
        return gpEns;
    }
}
